<?php

/**
 * Class StringBuilder
 *
 * This class tries to emulate a simple StringBuilder
 * @link http://www.pedrocorreia.net/mySnippets/php/A-Basic-PHP-StringBuilder
 * @author pedrocorreia.net
 */
class StringBuilder {

  private $_result;
  /**
   * Constructor
   */
  public function __construct() {
    $this->_result = array();
  }

  /**
   * Destructor
   */
  public function __destruct() {
    $this->_clearElements();
  }

  /**
   * Append Line
   *
   * @param String
   */
  public function append($value) {
    $this->_addElement($value);
  }

  /**
   * Append Line including "\n" at the beginning
   *
   * @param String
   */
  public function appendLine($value) {
    $this->_addElement("\r\n$value");
  }

  /**
   * Insert element at a specific position
   *
   * @param Int Position
   * @param String value
   */
  public function insert($pos, $value) {
    $this->_addElement($value, $pos - 1);
  }

  /**
   * Switch Elements
   *
   * @param Int 1st Position
   * @param Int 2nd Position
   */
  public function exchange($from, $to) {
    $this->_switchElements($from - 1, $to - 1);
  }

  /**
   * Update Element
   *
   * @param String Value
   * @param Int Position
   */
  public function update($value, $pos) {
    $this->_updateElement($pos - 1, $value);
  }

  /**
   * Replace Element (Alias of Update)
   *
   * @param Int Position
   * @param String
   */
  public function replace($pos, $value) {
    $this->Update($value, $pos);
  }

  /**
   * Get number of Elements
   *
   * @return Int
   */
  public function count() {
    return sizeof($this->_result);
  }

  /**
   * Get total String Length
   *
   * @return Int
   */
  public function length() {
    $count = $this->Count();
    $sum = 0;
    for ($i = 0; $i < $count; $i++) {
      $sum+=strlen($this->_getElement($i));
    }
    return $sum;
  }

  /**
   * Get specific Element
   *
   * @param Int Position
   * @return String
   */
  public function element($pos) {
    try {
      $elem = $this->_getElement($pos - 1);
    } catch (Exception $ex) {
      $elem = "Exception: $ex";
    }
    return $elem;
  }

  /**
   * Remove element
   *
   * @param Int Position
   * @return Bool
   */
  public function remove($pos) {
    $res = true;
    try {
      $this->_removeElement($pos - 1);
    } catch (Exception $ex) {
      $res = false;
    }
    return $res;
  }

  /**
   * Clear all elements
   *
   */
  public function clear() {
    $this->_clearElements();
  }

  /**
   * Convert StringBuilder to String
   *
   * @return String
   */
  public function toString() {
    $count = $this->Count();
    $str = "";
    for ($i = 0; $i < $count; $i++) {
      $str.= $this->_getElement($i);
    }
    return $str;
  }

  /**
   * Convert StringBuilder to String with format HTML
   *
   * @return String
   */
  public function toStringHTML() {
    $count = $this->Count();
    $str = "";
    for ($i = 0; $i < $count; $i++) {
      $str.=nl2br($this->_getElement($i));
    }
    return $str;
  }

  /**
   * Add/ Insert Element
   *
   * @param String
   * @param Int [Optional] Insert element at specific index
   *
   * note: when inserting element at specific index, if that index's
   * bigger than Elements Count, or invalid, i.e.,
   * negative or and inexistent index;
   * the value will be appended
   */
  private function _addElement($value, $pos=null) {
    if (!is_numeric($pos)) {
      $this->_result[] = $value;
    } else {
      if (!$this->_isValidIndex($pos))
        $pos = $this->Count();
      array_splice($this->_result, $pos, 0, $value);
    }
  }

  /**
   * Update Element
   *
   * @param Int Index
   * @param String Value
   */
  private function _updateElement($pos, $value) {
    if (!$this->_isValidIndex($pos))
      throw new Exception("OUT_OF_BOUNDS");
    $this->_result[$pos] = $value;
  }

  /**
   * Get Element
   *
   * @param Int Index
   * @return String
   */
  private function _getElement($pos) {
    if (!$this->_isValidIndex($pos))
      throw new Exception("OUT_OF_BOUNDS");
    return $this->_result[$pos];
  }

  /**
   * Remove Element
   *
   * @param Int Index
   */
  private function _removeElement($pos) {
    if (!$this->_isValidIndex($pos))
      throw new Exception("OUT_OF_BOUNDS");
    unset($this->_result[$pos]);
    $this->_result = array_values($this->_result);
  }

  /**
   * Switch Elements
   *
   * @param Int 1st Index
   * @param Int 2nd Index
   */
  private function _switchElements($pos1, $pos2) {
    if (!$this->_isValidIndex($pos1) || !$this->_isValidIndex($pos2)) {
      throw new Exception("OUT_OF_BOUNDS");
    }
    $aux = $this->_getElement($pos2);
    $this->_result[$pos2] = $this->_getElement($pos1);
    $this->_result[$pos1] = $aux;
  }

  /**
   * Clear StringBuilder
   *
   */
  private function _clearElements() {
    unset($this->_result);
  }

  /**
   * Check if the index is valid
   *
   * @param Int Index to examine
   * @return Bool
   */
  private function _isValidIndex($num) {
    return ($num < $this->Count() && $num >= 0);
  }

}

?>